const logger = require('../utils/logger');

// 自定义错误类
class AppError extends Error {
  constructor(message, statusCode) {
    super(message);
    this.statusCode = statusCode;
    this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';
    this.isOperational = true;

    Error.captureStackTrace(this, this.constructor);
  }
}

// 处理 MongoDB 重复键错误
const handleDuplicateFieldsDB = (err) => {
  const value = err.errmsg.match(/(["])(\\?.)*?\1/)[0];
  const message = `重复的字段值: ${value}，请使用其他值`;
  return new AppError(message, 400);
};

// 处理 MongoDB 验证错误
const handleValidationErrorDB = (err) => {
  const errors = Object.values(err.errors).map(el => el.message);
  const message = `无效的输入数据: ${errors.join('. ')}`;
  return new AppError(message, 400);
};

// 处理 MongoDB CastError
const handleCastErrorDB = (err) => {
  const message = `无效的 ${err.path}: ${err.value}`;
  return new AppError(message, 400);
};

// 处理 JWT 错误
const handleJWTError = () =>
  new AppError('无效的令牌，请重新登录', 401);

// 处理 JWT 过期错误
const handleJWTExpiredError = () =>
  new AppError('令牌已过期，请重新登录', 401);

// 发送开发环境错误
const sendErrorDev = (err, res) => {
  res.status(err.statusCode).json({
    status: err.status,
    error: err,
    message: err.message,
    stack: err.stack
  });
};

// 发送生产环境错误
const sendErrorProd = (err, res) => {
  // 操作性错误，可信任的错误：发送消息给客户端
  if (err.isOperational) {
    res.status(err.statusCode).json({
      status: err.status,
      message: err.message
    });
  } else {
    // 编程或其他未知错误：不泄露错误详情
    logger.error('ERROR:', err);
    
    res.status(500).json({
      status: 'error',
      message: '服务器内部错误'
    });
  }
};

// 全局错误处理中间件
const globalErrorHandler = (err, req, res, next) => {
  err.statusCode = err.statusCode || 500;
  err.status = err.status || 'error';

  if (process.env.NODE_ENV === 'development') {
    sendErrorDev(err, res);
  } else {
    let error = { ...err };
    error.message = err.message;

    // MongoDB 错误处理
    if (error.code === 11000) error = handleDuplicateFieldsDB(error);
    if (error.name === 'ValidationError') error = handleValidationErrorDB(error);
    if (error.name === 'CastError') error = handleCastErrorDB(error);
    if (error.name === 'JsonWebTokenError') error = handleJWTError();
    if (error.name === 'TokenExpiredError') error = handleJWTExpiredError();

    sendErrorProd(error, res);
  }
};

module.exports = {
  AppError,
  globalErrorHandler
};